package com.huihua.it.service;

import com.huihua.it.enums.I18nEnum;
import com.huihua.it.mapper.ResourceMapper;
import com.huihua.it.vo.ResourceVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.support.AbstractMessageSource;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.web.servlet.support.RequestContextUtils;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Service("messageSource")
@Slf4j
public class MyMessageSource extends AbstractMessageSource implements ResourceLoaderAware {

    // 这个是用来缓存数据库中获取到的配置的 数据库配置更改的时候可以调用reload方法重新加载
    ResourceLoader resourceLoader;

    // 本地缓存
    private static final Map LOCAL_CACHE = new ConcurrentHashMap<>(256);

    @Autowired
    private ResourceMapper configI18nMapper;

    @Autowired
    private HttpServletRequest request;

    /** 初始化
     * Java中该注解的说明：@PostConstruct该注解被用来修饰一个非静态的void()方法。
     * 被@PostConstruct修饰的方法会在服务器加载Servlet的时候运行，并且只会被服务器执行一次。
     * PostConstruct在构造函数之后执行，init()方法之前执行。
     */
    @PostConstruct
    public void init() {
        log.info("============init=============");
        this.reload();
    }

    /*** 重新将数据库中的国际化配置加载*/
    public void reload() {
        LOCAL_CACHE.clear();
        LOCAL_CACHE.putAll(loadAllMessageResourcesFromDB());
    }

    /** 从数据库中获取所有国际化配置 这边可以根据自己数据库表结构进行相应的业务实现
     * 对应的语言能够取出来对应的值就行了 无需一定要按照这个方法来
     */
    public Map loadAllMessageResourcesFromDB() {
        //获取数据库配置
        List<ResourceVO> list = configI18nMapper.selectLangList();
        if(!CollectionUtils.isEmpty(list)) {
            int len = list.size();
            // zh_CN
            final Map zhCnMessageResources = new HashMap<>(len);
            // en_US
            final Map enUsMessageResources = new HashMap<>(len);
            // zh_TW
            final Map zhTwMessageResources = new HashMap<>(len);
            // ko_KR
            final Map koKrMessageResources = new HashMap<>(len);
            // ja_JP
            final Map jaJpMessageResources = new HashMap<>(len);

            for(ResourceVO item : list) {
                //根据不同语言，分配到对应语言的值中
                switch (I18nEnum.fromKey(item.getLanguage())){
                    case ZH_CN:
                        zhCnMessageResources.put(item.getName(), item.getText());
                        break;
                    case EN_US:
                        enUsMessageResources.put(item.getName(), item.getText());
                        break;
                    case ZH_TW:
                        zhTwMessageResources.put(item.getName(), item.getText());
                        break;
                    case KO_KR:
                        koKrMessageResources.put(item.getName(), item.getText());
                        break;
                    case JA_JP:
                        jaJpMessageResources.put(item.getName(), item.getText());
                        break;
                    default:
                        break;
                }
//                if (item.getLanguage().equals(I18nEnum.ZH_CN.getKey())){
//                    zhCnMessageResources.put(item.getName(), item.getText());
//                }else if (item.getLanguage().equals(I18nEnum.EN_US.getKey())){
//                    enUsMessageResources.put(item.getName(), item.getText());
//                }else if (item.getLanguage().equals(I18nEnum.ZH_TW.getKey())){
//                    zhTwMessageResources.put(item.getName(), item.getText());
//                }
            }//加入缓存
            LOCAL_CACHE.put("cn", zhCnMessageResources);
            LOCAL_CACHE.put("en", enUsMessageResources);
            LOCAL_CACHE.put("tw", zhTwMessageResources);
        }
        return new HashMap<>();

    }

    public Map getSourceFromCacheMap(String code, Locale locale){
        String language = locale == null ? RequestContextUtils.getLocale(request).getLanguage() : locale.getLanguage();
        //获取缓存中对应语言的所有数据项
        Map props = (Map) LOCAL_CACHE.get(language);
        return props;
    }

    /** 从缓存中取出国际化配置对应的数据 或者从父级获取
     *@paramcode
     *@paramlocale 可以为null, 表示从当前HttpServletRequest中获取语言
     *@return
     */
    public String getSourceFromCache(String code, Locale locale) {
        String language = locale == null ? RequestContextUtils.getLocale(request).getLanguage() : locale.getLanguage();
        //获取缓存中对应语言的所有数据项
        Map props = (Map) LOCAL_CACHE.get(language);
        if (null!=props && props.containsKey(language)) {
            //如果对应语言中能匹配到数据项，那么直接返回
            return (String) props.get(code);
        }else{
            //如果对应语言中不能匹配到数据项，从上级获取返回
            try{
                if (null != this.getParentMessageSource()) {
                    return this.getParentMessageSource().getMessage(code, null, locale);
                }
            }catch(Exception ex) {
                logger.error(ex.getMessage(), ex);
            }
            //如果上级也没有找到，那么返回请求键值
            return code;
        }
    }

    //下面三个重写的方法是比较重要的
    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = (resourceLoader == null ? new DefaultResourceLoader() : resourceLoader);
    }

    @Override
    protected MessageFormat resolveCode(String code, Locale locale) {
        String msg=getSourceFromCache(code, locale);
        MessageFormat messageFormat= new MessageFormat(msg, locale);
        return messageFormat;
    }

    @Override
    protected String resolveCodeWithoutArguments(String code, Locale locale) {
        return getSourceFromCache(code, locale);
    }

}
